﻿
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace WpfToolkit
{
    [TemplatePart(Name = PartEditor, Type = typeof(AutoCompleteTextBox))]
    [TemplatePart(Name = PartItems, Type = typeof(ItemsControl))]
    [TemplatePart(Name = PartCheckedItems, Type = typeof(ItemsControl))]
    public class TagPannel : Control
    {
        #region 变量
        public const string PartEditor = "PART_Editor";
        public const string PartCheckedItems = "PART_CheckedItems";
        public const string PartItems = "PART_Items";
        private List<object> _checkedItems;
        private bool _isUpdatingItem;
        #endregion

        #region 构造函数
        static TagPannel()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(TagPannel), new FrameworkPropertyMetadata(typeof(TagPannel)));
        }
        #endregion


        #region 依赖属性

        public IEnumerable ItemsSource
        {
            get { return (IEnumerable)GetValue(ItemsSourceProperty); }
            set { SetValue(ItemsSourceProperty, value); }
        }

        // Using a DependencyProperty as the backing store for CheckedItems.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ItemsSourceProperty =
            DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(TagPannel),
                new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

        public string DisplayMemberPath
        {
            get { return (string)GetValue(DisplayMemberPathProperty); }
            set { SetValue(DisplayMemberPathProperty, value); }
        }

        // Using a DependencyProperty as the backing store for DisplayMemberPath.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DisplayMemberPathProperty =
            DependencyProperty.Register("DisplayMemberPath", typeof(string), typeof(TagPannel),
                new FrameworkPropertyMetadata(string.Empty));

        public string GroupName
        {
            get { return (string)GetValue(GroupNameProperty); }
            set { SetValue(GroupNameProperty, value); }
        }

        // Using a DependencyProperty as the backing store for DisplayMemberPath.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty GroupNameProperty =
            DependencyProperty.Register("GroupName", typeof(string), typeof(TagPannel),
                new FrameworkPropertyMetadata(string.Empty));

        public IEnumerable CheckedItems
        {
            get { return (IEnumerable)GetValue(CheckedItemsProperty); }
            set { SetValue(CheckedItemsProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ChekedItems.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CheckedItemsProperty =
            DependencyProperty.Register("CheckedItems", typeof(IEnumerable), typeof(TagPannel),
                new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnCheckedItemsChanged)));

        public DataTemplate CheckedItemTemplate
        {
            get { return (DataTemplate)GetValue(CheckedItemTemplateProperty); }
            set { SetValue(CheckedItemTemplateProperty, value); }
        }

        // Using a DependencyProperty as the backing store for CheckedItemTemplate.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CheckedItemTemplateProperty =
            DependencyProperty.Register("CheckedItemTemplate", typeof(DataTemplate), typeof(TagPannel), new PropertyMetadata(null));

        public int ItemsControlHeight
        {
            get { return (int)GetValue(ItemsControlHeightProperty); }
            set { SetValue(ItemsControlHeightProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ItemsControlHeight.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ItemsControlHeightProperty =
            DependencyProperty.Register("ItemsControlHeight", typeof(int), typeof(TagPannel), new PropertyMetadata(200));


        public DataTemplate ItemTemplate
        {
            get { return (DataTemplate)GetValue(ItemTemplateProperty); }
            set { SetValue(ItemTemplateProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ItemTemplate.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ItemTemplateProperty =
            DependencyProperty.Register("ItemTemplate", typeof(DataTemplate), typeof(TagPannel), new PropertyMetadata(null));

        public DataTemplate PopupItemTemplate
        {
            get { return (DataTemplate)GetValue(PopupItemTemplateProperty); }
            set { SetValue(PopupItemTemplateProperty, value); }
        }

        // Using a DependencyProperty as the backing store for PopupItemTemplate.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty PopupItemTemplateProperty =
            DependencyProperty.Register("PopupItemTemplate", typeof(DataTemplate), typeof(TagPannel), new PropertyMetadata(null));

        public ISuggestionProvider Provider
        {
            get { return (ISuggestionProvider)GetValue(ProviderProperty); }
            set { SetValue(ProviderProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Provider.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ProviderProperty =
            DependencyProperty.Register("Provider", typeof(ISuggestionProvider), typeof(TagPannel), new PropertyMetadata(null));

        public AutoCompleteTextBox AutoTxtEditor { get; set; }

        public ItemsControl ItemsControl { get; set; }

        public ItemsControl CheckedItemsControl { get; set; }

        #endregion

        #region 方法

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            AutoTxtEditor = Template.FindName(PartEditor, this) as AutoCompleteTextBox;
            ItemsControl = Template.FindName(PartItems, this) as ItemsControl;
            CheckedItemsControl = Template.FindName(PartCheckedItems, this) as ItemsControl;

            //GotFocus += TagPannel_GotFocus;

            if (AutoTxtEditor != null)
            {
                AutoTxtEditor.KeyUp += Editor_KeyUp;
                AutoTxtEditor.ItemsSelectorPreviewMouseDown += AutoTxtEditor_ItemsSelectorPreviewMouseDown;
            }

            if (ItemsControl != null)
            {
                ItemsControl.PreviewMouseLeftButtonUp += ItemsControl_PreviewMouseLeftButtonUp;
            }

            if (CheckedItemsControl != null)
            {
                CheckedItemsControl.PreviewMouseLeftButtonUp += CheckedItemsControl_PreviewMouseLeftButtonUp;
            }
        }


        private void TagPannel_GotFocus(object sender, RoutedEventArgs e)
        {
            AutoTxtEditor?.Focus();
        }

        private bool IsChooseCurrentItemKey(Key pressed)
        {
            return pressed == Key.Enter || pressed == Key.Return || pressed == Key.Tab;
        }

        private object GetAddItem(object value)
        {
            var obj = new System.Dynamic.ExpandoObject();
            ((IDictionary<string, object>)obj).Add(DisplayMemberPath, value);
            return obj;
        }

        private void Editor_KeyUp(object sender, KeyEventArgs e)
        {
            if (sender is AutoCompleteTextBox txt)
            {
                if (IsChooseCurrentItemKey(e.Key) && !string.IsNullOrEmpty(txt.Text) && !string.IsNullOrEmpty(DisplayMemberPath))
                {
                    if (txt.SelectedItem != null)
                    {
                        AddSelectedItem(txt.SelectedItem);
                        txt.SelectedItem = null;
                        txt.Text = null;
                    }
                    else
                    {
                        var item = txt.ItemsSelector.Items.CurrentItem;
                        AddSelectedItem(item);
                        txt.Text = null;
                    }

                }

                if (Keyboard.Modifiers == ModifierKeys.Shift)
                {
                    if (_checkedItems != null && string.IsNullOrEmpty(txt.Text) && e.Key == Key.Back)
                    {
                        RemoveCheckItem(_checkedItems.LastOrDefault());
                    }
                }

            }
        }

        private static void OnCheckedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is TagPannel tp && e.OldValue != e.NewValue)
            {
                tp._checkedItems = e.NewValue as List<object>;
                if (tp._checkedItems == null)
                {
                    tp.AutoTxtEditor.Text = string.Empty;
                }
            }
        }

        private void CheckedItemsControl_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (e.OriginalSource is CheckBox cb)
            {
                if (cb.IsChecked.HasValue && cb.IsChecked.Value == false)
                {
                    RemoveCheckItem(cb.DataContext);
                }
                AutoTxtEditor.Editor.Focus();
            }
        }

        private void AutoTxtEditor_ItemsSelectorPreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            if (AutoTxtEditor.SelectedItem != null)
            {
                AddSelectedItem(AutoTxtEditor.SelectedItem);
                AutoTxtEditor.SelectedItem = null;
                AutoTxtEditor.Text = null;
                _isUpdatingItem = true;
            }
        }

        /// <summary>
        /// 添加选中项目
        /// </summary>
        /// <param name="selectedItem"></param>
        private void AddSelectedItem(object selectedItem)
        {
            if (_checkedItems == null)
            {
                _checkedItems = new List<object>();
            }
            if (!_checkedItems.Contains(selectedItem))
            {
                _checkedItems.Add(selectedItem);
                CheckedItems = new List<object>(_checkedItems);
            }
        }

        private void RemoveCheckItem(object item)
        {
            if (_checkedItems == null)
            {
                return;
            }
            _checkedItems.Remove(item);
            CheckedItems = new List<object>(_checkedItems);
        }

        private void ItemsControl_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (e.OriginalSource is RadioButton rb)
            {
                if (rb.IsChecked.HasValue && rb.IsChecked.Value == true)
                {
                    if (_isUpdatingItem)
                    {
                        _isUpdatingItem = false;
                        rb.IsChecked = false;
                        return;
                    }
                    else
                    {
                        AddSelectedItem(rb.DataContext);
                        rb.IsChecked = false;
                    }
                }
                AutoTxtEditor.Editor.Focus();
            }
        }
        #endregion
    }
}
